/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.message.service.lark.impl;

import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.je.common.base.util.StringUtil;
import com.je.message.Push;
import com.je.message.ResultMsgResp;
import com.je.message.exception.MessageSendException;
import com.je.message.service.AbstractThirdPartyPush;
import com.je.message.service.lark.Code;
import com.je.message.service.lark.LarkMsgService;
import com.je.message.service.log.LogServiceFactory;
import com.je.message.vo.*;
import com.je.meta.util.SettingHelper;
import com.je.meta.util.setting.Open.OpenFeiSu;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import java.util.*;


@Service
public class LarkMsgServiceImpl extends AbstractThirdPartyPush implements LarkMsgService {

    final String GET_TOKEN_URL = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/";
    final String SEND_MSG_URL = "https://open.feishu.cn/open-apis/message/v4/batch_send/";
    /**
     * 获取token
     *
     * @param  //app | tenant | user
     * @return java.lang.String
     */
    public String getToken()  {
        JSONObject params = null;
            OpenFeiSu openFeiSuConfigConfig = SettingHelper.getOpenFeiSuConfig();
            String appId = StringUtil.getDefaultValue(openFeiSuConfigConfig.getAppId(), "");
            String appSecret = StringUtil.getDefaultValue(openFeiSuConfigConfig.getAppSecret(), "");
            params.put("app_id", appId);
            params.put("app_secret", appSecret);

        JSONObject result = requestPost(GET_TOKEN_URL,  params, null);
        //设置token
        String tenantAccessToken = result.getString("tenant_access_token");
        return tenantAccessToken;
    }

    @Override
    public ResultMsgResp send(MessageVo msgVo)  {
        //校验系统设置有没有勾选
        checkService(thirdPartyType.LARK);

        String token = getToken();

        List<String> fsUserIds = getUserIds(msgVo, thirdPartyType.LARK);
        JSONObject result = batchSend(msgVo.getTitle(), msgVo.getContent(), msgVo.getButtonText(), msgVo.getUrl(), fsUserIds, token);
        //api调用响应码
        Integer code = result.getInteger("code");
        //响应码枚举
        Code check = Code.check(code);

        //成功
        if (check.equals(Code.SUCCESS)) {
            msgVo.setResult(result.getString("msg"));
            LogServiceFactory.newLog(LogServiceFactory.LogTypeEnum.OTHER).saveAllLog(msgVo, Push.LARK);
            return new ResultMsgResp(200, "发送成功", true);
        }
        logger.info("飞书推送消息失败！详情为" + result.getString("msg"));
        msgVo.setErrorMessage(result.getString("msg"));
        LogServiceFactory.newLog(LogServiceFactory.LogTypeEnum.OTHER).saveAllLog(msgVo, Push.LARK);
        return new ResultMsgResp(9999, "发送失败！", false);
    }

    /**
     * 批量消息发送
     *
     * @param title      标题
     * @param content    内容
     * @param buttonText 按钮
     * @param buttonUrl  点击链接
     * @param userIds    发送人
     * @return com.alibaba.fastjson.JSONObject
     */
    public JSONObject batchSend(String title, String content, String buttonText, String buttonUrl, List<String> userIds, String token)  {

        //添加参数
        JSONObject params = new JSONObject();
        params.put("user_ids", userIds);
        params.put("msg_type", "interactive");

        //卡片
        JSONObject card = new JSONObject();
        card.put("config", JSON.parseObject("{\"wide_screen_mode\": true}"));
        card.put("header", JSON.parseObject(String.format("{\"title\":{\"tag\":\"plain_text\",\"content\":\"%s\"}}", title)));

        //卡片内容
        JSONArray elements = new JSONArray();
        elements.add(JSON.parseObject(String.format("{\"tag\":\"div\",\"text\":{\"tag\":\"plain_text\",\"content\":\"%s\"}}", content)));

        //按钮
        if (StringUtils.isNotBlank(buttonText)) {
            JSONObject button = JSON.parseObject(String.format(
                    "{\"tag\":\"action\",\"actions\":[{\"tag\":\"button\",\"text\":{\"tag\":\"plain_text\",\"content\":\"%s\"},\"multi_url\":{\"url\":\"%s\"}}]}",
                    buttonText, buttonUrl));
            elements.add(button);
        }

        card.put("elements", elements);
        params.put("card", card);

        //请求结果
        String sendUrl = SEND_MSG_URL;
        JSONObject result = requestPost(sendUrl,  params, token);
        //有效数据
        return result;
    }

    /**
     * post请求 默认重试3次
     *
     * @param api       api接口枚举
     * @param params    请求参数
     * @return JSONObject 相应结果
     */
    private JSONObject requestPost(String api,   JSONObject params, String token)  {
        return request(api,  params, true, 3, token);
    }

    /**
     * 请求api
     *
     * @param api       api接口枚举
     * @param params    请求参数
     * @param isPost    是否post
     * @param retry     重试次数
     * @return JSONObject 相应结果
     */
    private JSONObject request(String api,   JSONObject params, boolean isPost, int retry, String token)  {

        //初始化请求
        HttpRequest request;
        if (isPost) {
            request = HttpRequest.post(api);
            //设置请求参数
            if (params != null && !params.isEmpty()) {
                request.body(params.toJSONString());
            }
        } else {
            //拼接参数到url  HttpRequest自带的url拼接参数不满足API需求
            String url = getUrl(api, params);
            request = HttpRequest.get(url);
        }

        //设置响应类型为json
        request.header("Content-Type", "application/json");
        //设置请求头信息 !!! 注意空格不可删除 !!!
//        String token = getToken();
        if (token != null && token.trim().length() != 0) {
            request.header("Authorization", "Bearer " + token);
        }

        logger.debug("url:{}", request.getUrl());
        logger.debug("token:{}", token);
        logger.debug("params:{}", params == null ? "empty" : params);

        //发送请求
        cn.hutool.http.HttpResponse execute = request.execute();

        //获取响应结果
        JSONObject result = JSON.parseObject(execute.body());
        //api调用响应码
        Integer code = result.getInteger("code");
        //响应码枚举
        Code check = Code.check(code);

        //成功
        if (check.equals(Code.SUCCESS)) {
            return result;
        }

        //重试次数不为0
        if (retry != 0) {
            if (check.equals(Code.tenant_access_token_invalid)) {
                //tenant access token 无效 重新获取
                token = getToken();
            } else if (check.equals(Code.over_frequency)) {
                //请求过于频繁 等待一秒
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //重试
            logger.info("retry " + api);
            return request(api,  params, isPost, retry - 1, token);
        }

        //抛出异常附带完整响应信息
        throw new MessageSendException(request.getUrl() + "-" + code + ":" + check.getMsg() + "-" + result);
    }


    /**
     * 拼接getUrl
     * <p>
     * 将参数追加到url
     *
     * @param paramMap 表单数据
     * @return url参数
     */
    public String getUrl(String url, Map<String, Object> paramMap) {
        //校验参数
        if (paramMap == null || paramMap.isEmpty()) {
            return url;
        }
        final StringBuilder sb = new StringBuilder(url);
        if (!url.contains("?")) {
            sb.append("?");
        }
        String key;
        Object value;
        for (Map.Entry<String, ?> item : paramMap.entrySet()) {
            key = item.getKey();
            value = item.getValue();

            //集合形式特殊处理
            if (value instanceof Collection) {
                for (Object v : (Collection) value) {
                    sb.append("&").append(key).append("=").append(v.toString());
                }
            } else {
                sb.append("&").append(key).append("=").append(value.toString());
            }
        }
        return sb.toString();
    }
}
